package com.spider.service;

import com.spider.pipeline.ListPagePipeline;
import com.spider.processor.ListPageProcessor;
import com.spider.entites.Filed;
import com.spider.entites.Task;
import com.spider.entites.TaskSetting;
import com.spider.mapper.ConfMapper;
import com.spider.mapper.TaskMapper;
import com.spider.runnable.ListPageRunnable;
import com.spider.to.TaskTo;
import com.spider.utils.JsonUtils;
import com.spider.utils.StringUtils;
import com.spider.vo.PageVo;
import com.spider.vo.TaskVo;
import org.apache.commons.dbutils.QueryRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author panglin
 * @date 2020/11/20 14:40
 */
@Service
public class TaskService {

    private static final Logger LOGGER = LoggerFactory.getLogger(TaskService.class);

    private static final ThreadPoolExecutor POOL = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), (r) -> {
        Thread thread = new Thread(r);
        thread.setName("--listTask--pool--" + thread.getId());
        thread.setUncaughtExceptionHandler((t, e) -> e.printStackTrace());
        return thread;
    });

    @Resource
    private TaskMapper taskMapper;

    @Resource
    private ConfMapper confMapper;

    @Resource
    private QueryRunner queryRunner;

    @Resource
    private ListPageRunnable runnable;

    @Resource
    private ListPagePipeline pipeline;

    @Resource
    private ListPageProcessor processor;

    /**
     * 添加任务
     *
     * @param task
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean addTask(TaskTo task) {
        Task t = new Task();
        t.setCreateTime(new Timestamp(System.currentTimeMillis()));
        t.setIndexPage(task.getIndexPage());
        t.setNextXp(task.getNextXp());
        t.setTaskName(task.getTaskName());
        t.setTbName(task.getTbName());
        t.setListXp(task.getListXp());
        Filed filed = new Filed();
        filed.setId(CollectionUtils.isEmpty(task.getListFiled()) ? 0 : task.getListFiled().size());
        filed.setXpath(task.getListXp());
        filed.setFiled("pageUrl");
        task.getListFiled().add(filed);
        t.setListFiled(JsonUtils.writeValueAsString(task.getListFiled()));
        t.setDetailFiled(JsonUtils.writeValueAsString(task.getDetailFiled()));
        Integer table = this.taskMapper.createTable(task.getTbName(), this.getFiled(t));
        Integer flag = this.taskMapper.addTask(t);
        Integer conf = this.confMapper.addConf(t.getId());
        LOGGER.info("任务添加状态：{}，{}，{}", table == 0 ? "数据表已创建" : "数据表创建失败", flag == 1 ? "任务已添加" : "任务添加失败", conf == 1 ? "配置已生成" : "配置生成失败");
        return flag == 1 && conf == 1 && table == 0;
    }

    /**
     * 获取任务
     *
     * @param info
     * @param size
     * @param num
     * @return
     */
    public PageVo getTasks(String info, Integer size, Integer num) {
        if (StringUtils.isNotEmpty(info)) {
            info = "%" + info + "%";
        }
        num = (num - 1) * size;
        List<Task> tasks = this.taskMapper.getTasks(info, size, num);
        if (tasks.size() == 0) {
            return null;
        }
        List<TaskVo> taskVos = new LinkedList<>();
        tasks.forEach(t -> {
            TaskVo tv = new TaskVo();
            switch (t.getRunStatus()) {
                case 0:
                    tv.setRunStatus("未开始");
                    break;
                case 1:
                    tv.setRunStatus("运行中");
                    break;
                case 2:
                    tv.setRunStatus("已完成");
                default:
                    break;
            }
            if (t.getBeginTime() == null || t.getEndTime() == null) {
                tv.setTakeTime("--");
            } else {
                tv.setTakeTime((t.getEndTime().getTime() - t.getBeginTime().getTime()) / 1000 + "秒");
            }
            tv.setId(t.getId());
            tv.setSpiderNum(t.getSpiderNum());
            tv.setCreateTime(t.getCreateTime());
            tv.setTaskName(t.getTaskName());
            tv.setTbName(t.getTbName());
            taskVos.add(tv);
        });
        return new PageVo(taskVos, this.taskMapper.getTaskTotal());
    }

    /**
     * 拼接字段
     *
     * @param task
     */
    private String getFiled(Task task) {
        List<Filed> listFiled = JsonUtils.transformJsonToListFiled(task.getListFiled());
        List<Filed> detailFiled = JsonUtils.transformJsonToListFiled(task.getDetailFiled());
        StringBuilder sb = new StringBuilder();
        if (listFiled.size() != 0) {
            listFiled.forEach(f -> sb.append(f.getFiled()).append(" TEXT NULL,"));
            if (detailFiled.size() != 0) {
                detailFiled.forEach(f -> sb.append(f.getFiled()).append(" TEXT NULL,"));
            }
        }
        return sb.toString();
    }

    /**
     * 删除任务和表格
     *
     * @param tid
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteTask(Integer tid) {
        Task status = this.taskMapper.queryRunStatusById(tid);
        if (status.getRunStatus() == 1) {
            return false;
        }
        Integer deleteTaskById = this.taskMapper.deleteTaskById(tid);
        Integer deleteConf = this.confMapper.deleteConf(tid);
        this.taskMapper.deleteTable(status.getTbName());
        LOGGER.info("当前任务已删除,任务id:{},表格:{}", tid, status.getTbName());
        return deleteTaskById == 1 && deleteConf == 1;
    }

    /**
     * 异步启动任务
     *
     * @param tid
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean begin(Integer tid) {
        Task task = this.taskMapper.queryTaskById(tid);
        if (task.getRunStatus() == 1 || task.getRunStatus() == 2) {
            return false;
        }
        this.taskMapper.createTable(task.getTbName(), this.getFiled(task));
        TaskSetting setting = this.confMapper.querySetByTid(tid);
        String listFiled = task.getListFiled();
        List<Filed> filedList = JsonUtils.transformJsonToListFiled(listFiled);
        POOL.execute(this.runnable.initTask(this.queryRunner, this.processor.initPageProcessor(filedList, task.getNextXp(), setting), this.pipeline.initPipeline(filedList, task.getTbName(), this.queryRunner), task, setting.getThreadNum()));
        this.taskMapper.updateTaskStatus(tid, 1, new Timestamp(System.currentTimeMillis()));
        LOGGER.info("任务已开始,任务id:{}", tid);
        return true;
    }

    /**
     * 重置任务
     *
     * @param tid
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean resetTask(Integer tid) {
        Task task = this.taskMapper.queryTaskById(tid);
        if (task.getRunStatus() == 1) {
            return false;
        }
        Integer flag = this.taskMapper.resetTaskById(tid);
        if (flag == 1) {
            this.taskMapper.createTable(task.getTbName(), this.getFiled(task));
            this.taskMapper.flushTb(task.getTbName());
            LOGGER.info("任务已重置:{},数据表已清空:{}", tid, task.getTbName());
            return true;
        }
        return false;
    }

    /**
     * 获取单条任务信息以供修改
     *
     * @param id
     * @return
     */
    public TaskTo getTask(Integer id) {
        Task task = this.taskMapper.queryTaskById(id);
        if (task == null) {
            return null;
        }
        TaskTo to = new TaskTo();
        to.setId(id);
        to.setTaskName(task.getTaskName());
        to.setTbName(task.getTbName());
        to.setListXp(task.getListXp());
        to.setNextXp(task.getNextXp());
        to.setIndexPage(task.getIndexPage());
        List<Filed> filedList = JsonUtils.transformJsonToListFiled(task.getListFiled());
        assert filedList != null;
        filedList.removeIf(f -> "pageUrl".equals(f.getFiled()));
        to.setListFiled(filedList);
        to.setDetailFiled(JsonUtils.transformJsonToListFiled(task.getDetailFiled()));
        return to;
    }

    /**
     * 修改任务
     *
     * @param task
     * @return
     */
    public Integer updateTask(TaskTo task) {
        Task t = new Task();
        t.setId(task.getId());
        t.setTaskName(task.getTaskName());
        t.setIndexPage(task.getIndexPage());
        Filed filed = new Filed();
        filed.setId(task.getListFiled().size() == 0 ? 0 : task.getListFiled().size());
        filed.setFiled("pageUrl");
        filed.setXpath(task.getListXp());
        task.getListFiled().add(filed);
        t.setListXp(task.getListXp());
        t.setNextXp(task.getNextXp());
        t.setListFiled(JsonUtils.writeValueAsString(task.getListFiled()));
        t.setDetailFiled(JsonUtils.writeValueAsString(task.getDetailFiled()));
        return this.taskMapper.updateTaskById(t);
    }

    /**
     * 查询任务参数
     *
     * @param id
     * @return
     */
    public TaskSetting querySettingById(Integer id) {
        return this.confMapper.querySetByTid(id);
    }

    /**
     * 设置配置参数
     *
     * @param setting
     * @return
     */
    public Boolean updateSetting(TaskSetting setting) {
        Integer flag = this.confMapper.updateSettingByTid(setting);
        LOGGER.info("任务参数已修改,任务id:{}", setting.getTid());
        return flag == 1;
    }
}
